#include "ap.h"

#include "mem.h"

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <limits.h>

static void
test_simple()
{
    AP_T x =  AP_new(123);
    long val = AP_toint(x);
    assert(val == 123);
    AP_free(&x);

    x =  AP_new(LONG_MIN);
    val = AP_toint(x);
    assert(val == LONG_MIN);
    assert(AP_cmpi(x, LONG_MIN) == 0);
    AP_free(&x);

    x =  AP_new(LONG_MAX);
    val = AP_toint(x);
    assert(val == LONG_MAX);
    AP_free(&x);
}

static void
test_str()
{
    const char *str = "12345678";
    char *end;
    AP_T x;
    long val;
    int ret;

    x = AP_fromstr(str, 10, &end);
    assert(str + strlen(str) == end);

    char buf[1024];
    char *ptr;
    ptr = AP_tostr(buf, 1024, 10, x);
    assert(ptr == buf);
    assert(strcmp(buf, str) == 0);

    ptr = AP_tostr(NULL, 0, 10, x);
    assert(strcmp(buf, str) == 0);
    FREE(ptr);

    Fmt_register('a', AP_fmt);
    ret = Fmt_sfmt(buf, 1024, "%a", x);
    assert(strcmp(buf, str) == 0);
    assert(ret == (int)strlen(str));

    val = AP_toint(x);
    assert(val == 12345678);

    AP_T x2 = AP_new(val);
    assert(AP_cmp(x, x2) == 0);
    assert(AP_cmpi(x, val) == 0);

    AP_free(&x);
    AP_free(&x2);


    const char *str2 = "-12345678";
    x = AP_fromstr(str2, 10, &end);
    assert(str2 + strlen(str2) == end);

    ptr = AP_tostr(buf, 1024, 10, x);
    assert(ptr == buf);
    assert(strcmp(buf, str2) == 0);

    val = AP_toint(x);
    assert(val == -12345678);

    assert(AP_cmpi(x, val) == 0);

    AP_free(&x);


    // test convert
    x = AP_fromstr("1", 10, NULL);
    assert(AP_toint(x) == 1);
    AP_free(&x);

    x = AP_fromstr("12", 10, NULL);
    assert(AP_toint(x) == 12);
    AP_free(&x);

    x = AP_fromstr("-1", 10, NULL);
    assert(AP_toint(x) == -1);
    AP_free(&x);

    x = AP_fromstr("-12", 10, NULL);
    assert(AP_toint(x) == -12);
    AP_free(&x);
}

static void
test_add_sub()
{
    AP_T x, x2, x3, x4, x5;
    x = AP_new(LONG_MIN);
    x2 = AP_neg(x);

    x3 = AP_new(LONG_MAX);
    x4 = AP_addi(x3, 1);
    assert(AP_cmp(x4, x2) == 0);

    x5 = AP_subi(x4, 1);
    assert(AP_cmp(x5, x3) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);
    AP_free(&x4);
    AP_free(&x5);

    x  =  AP_new(8999);
    x2 = AP_new(10999);
    x3 = AP_sub(x, x2);
    assert(AP_cmpi(x3, -2000) == 0);
    
    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);
}

static void
test_mul_div()
{
    AP_T x, x2, x3, x4;
    x = AP_new(100*100*100*100+99);
    x2 = AP_new(100);
    x3 = AP_div(x, x2);
    assert(AP_cmpi(x3, 100*100*100) == 0);

    x4 = AP_mod(x, x2);
    assert(AP_cmpi(x4, 99) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);
    AP_free(&x4);


    x = AP_new(7);
    x2 = AP_divi(x, 3);
    assert(AP_cmpi(x2, 2) == 0);
    assert(AP_modi(x, 3) == 1);

    AP_free(&x);
    AP_free(&x2);


    x = AP_new(7);
    x2 = AP_divi(x, -3);
    assert(AP_cmpi(x2, -3) == 0);
    assert(AP_modi(x, -3) == 2);

    AP_free(&x);
    AP_free(&x2);
}

static void
test_pow()
{
    AP_T x, x2, x3, x4;
    x = AP_new(2);
    x2 = AP_new(11);
    x3 = AP_pow(x, x2, NULL);
    assert(AP_cmpi(x3, 2048) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);

    x = AP_new(2);
    x2 = AP_new(11);
    x3 = AP_new(2000);
    x4 = AP_pow(x, x2, x3);
    assert(AP_cmpi(x4, 48) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);
    AP_free(&x4);
}

static void
test_shift()
{
    AP_T x, x2, x3;
    x = AP_new(1);
    x2 = AP_lshift(x, 30);
    assert(AP_cmpi(x2, 1<<30) == 0);

    x3 = AP_rshift(x2, 30);
    assert(AP_cmpi(x3, 1) == 0);
    assert(AP_cmp(x3, x) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);


    x = AP_new(-1);
    x2 = AP_lshift(x, 30);
    assert(AP_cmpi(x2, -(1<<30)) == 0);

    x3 = AP_rshift(x2, 30);
    assert(AP_cmpi(x3, -1) == 0);
    assert(AP_cmp(x3, x) == 0);

    AP_free(&x);
    AP_free(&x2);
    AP_free(&x3);
}

int main()
{
    test_simple();
    test_str();
    test_add_sub();
    test_mul_div();
    test_pow();
    test_shift();
    return 0;
}

